import crypto from 'crypto'
import YzOperator from './yzOperator'
import YzDoubleOperator from './yzDoubleOperator'
import YzOperand from './yzOperand'

export function electOperator (opObj) {
  Object.setPrototypeOf(opObj, YzOperator.prototype)
  Object.setPrototypeOf(opObj.op, YzOperand.prototype)
  Object.setPrototypeOf(opObj.source, YzOperand.prototype)
  if (opObj.source && opObj.source.type === 'op') {
    electOperator(opObj.source.opValue)
  }
  Object.setPrototypeOf(opObj.target, YzOperand.prototype)
  if (opObj.target && opObj.target.type === 'op' && !!opObj.target.opValue) {
    electOperator(opObj.target.opValue)
  }
  if (opObj.type === 'double') {
    Object.setPrototypeOf(opObj, YzDoubleOperator.prototype)
    Object.setPrototypeOf(opObj.opBefore, YzOperand.prototype)
    Object.setPrototypeOf(opObj.targetBefore, YzOperand.prototype)
    if (opObj.targetBefore && opObj.targetBefore.type === 'op' && !!opObj.targetBefore.opValue) {
      electOperator(opObj.targetBefore.opValue)
    }
  }
  return opObj
}

export function map2Operator (operatorNum, label, avalue, bvalue, op, isStr) {
  if (op) {
    electOperator(op)
    return op
  }
  const dic = {
    1: new YzDoubleOperator(
      new YzOperand('variable', label),
      '>',
      new YzOperand('real', avalue),
      '<',
      new YzOperand('real', bvalue)
    ),
    4: new YzOperator(new YzOperand('variable', label), '<', new YzOperand('real', avalue)),
    5: new YzOperator(new YzOperand('variable', label), '>', new YzOperand('real', bvalue)),
    8: new YzOperator(new YzOperand('variable', label), '>=', new YzOperand('real', bvalue)),
    9: new YzOperator(new YzOperand('variable', label), '<=', new YzOperand('real', avalue)),
    10: new YzDoubleOperator(
      new YzOperand('variable', label),
      '>=',
      new YzOperand('real', avalue),
      '<',
      new YzOperand('real', bvalue)
    ),
    11: new YzDoubleOperator(
      new YzOperand('variable', label),
      '>',
      new YzOperand('real', avalue),
      '<=',
      new YzOperand('real', bvalue)
    ),
    12: new YzDoubleOperator(
      new YzOperand('variable', label),
      '>=',
      new YzOperand('real', avalue),
      '<=',
      new YzOperand('real', bvalue)
    ),
    16: new YzOperator(new YzOperand('variable', label), '!=', new YzOperand('real', avalue))
  }
  if (isStr) {
    dic[7] = new YzOperator(new YzOperand('variable', label), '==', new YzOperand('string', avalue))
  } else {
    dic[7] = new YzOperator(new YzOperand('variable', label), '==', new YzOperand('real', avalue))
  }
  dic[6] = new YzOperator(new YzOperand('op', dic[4]), '||', new YzOperand('op', dic[5]))
  dic[13] = new YzOperator(new YzOperand('op', dic[4]), '||', new YzOperand('op', dic[8]))
  dic[14] = new YzOperator(new YzOperand('op', dic[9]), '||', new YzOperand('op', dic[5]))
  dic[15] = new YzOperator(new YzOperand('op', dic[9]), '||', new YzOperand('op', dic[8]))
  return dic[operatorNum]
}

export function execOperator (map, op) {
  let vars = Object.entries(map).map(kv => {
    let hash = crypto.createHash('md5')
    return `let v_${hash.update(kv[0]).digest('HEX')} = ${kv[1]}`
  }).join('\r\n')
  let jsCode = `
  (() => {
    ${vars}
return (${op.getExecJsString()}) ()
  }) ()
  `
  // eslint-disable-next-line no-eval
  return eval(jsCode)
}
